/*
 * @(#)LogstashEncoder.java
 * Date 2013-03-30
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.logging;

import java.io.IOException;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import java.util.Map;

import java.util.Map.Entry;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;

import ch.qos.logback.core.CoreConstants;

import ch.qos.logback.core.encoder.EncoderBase;

import org.codehaus.jackson.JsonGenerator.Feature;

import org.codehaus.jackson.map.ObjectMapper;

import org.codehaus.jackson.node.ObjectNode;


/**
 *
 * @author jim
 */
public class LogstashEncoder extends EncoderBase<ILoggingEvent> {

    /** Field description */
    private static final ObjectMapper JSON_MAPPER =
        new ObjectMapper().configure(Feature.ESCAPE_NON_ASCII, true);

    /** Field description */
    private boolean immediateFlush = true;

    /**
     * Method description
     *
     *
     * @throws IOException
     */
    @Override
    public void close() throws IOException {

        this.outputStream.write(CoreConstants.LINE_SEPARATOR.getBytes());
    }

    /**
     * Method description
     *
     *
     * @param event
     *
     * @return
     */
    private ObjectNode createFields(ILoggingEvent event) {

        ObjectNode fieldsNode = JSON_MAPPER.createObjectNode();

        fieldsNode.put("logger_name", event.getLoggerName());
        fieldsNode.put("thread_name", event.getThreadName());
        fieldsNode.put("level", event.getLevel().toString());
        fieldsNode.put("level_value", event.getLevel().toInt());

        IThrowableProxy throwableProxy = event.getThrowableProxy();

        if (throwableProxy != null) {

            fieldsNode.put("stack_trace",
                           ThrowableProxyUtil.asString(throwableProxy));
        }

        Map<String, String> mdc = event.getMDCPropertyMap();

        for (Entry<String, String> entry : mdc.entrySet()) {

            String key = entry.getKey();
            String value = entry.getValue();

            fieldsNode.put(key, value);
        }

        return fieldsNode;

    }

    /**
     * Method description
     *
     *
     * @param event
     *
     * @throws IOException
     */
    @Override
    public void doEncode(ILoggingEvent event) throws IOException {

        ObjectNode eventNode = JSON_MAPPER.createObjectNode();

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

        eventNode.put("@timestamp", df.format(event.getTimeStamp()));
        eventNode.put("@message", event.getFormattedMessage());
        eventNode.put("@fields", createFields(event));

        this.outputStream.write(JSON_MAPPER.writeValueAsBytes(eventNode));
        this.outputStream.write(CoreConstants.LINE_SEPARATOR.getBytes());

        if (immediateFlush) {
            this.outputStream.flush();
        }

    }

    /**
     * Method description
     *
     *
     * @return
     */
    public boolean isImmediateFlush() {
        return immediateFlush;
    }

    /**
     * Method description
     *
     *
     * @param immediateFlush
     */
    public void setImmediateFlush(boolean immediateFlush) {
        this.immediateFlush = immediateFlush;
    }
}
